#pragma once
#include <functional>
#include <pthread.h>
#include "Log.hpp"
#include "Sock.hpp"
#include "Protocol.hpp"

using namespace ns_Protocol;

namespace ns_Server
{
    const uint16_t defaultPort = 8885;
    class tcp_server;
    using func_t = std::function<ns_Protocol::Response(ns_Protocol::Request&)>;
    
    class ThreadData
    {
    public:
        ThreadData(int sock, std::string &clientip, uint16_t clientport, tcp_server *tserp) : 
        _sock(sock),
        _clientip(clientip),
        _clientport(clientport),
        _tserp(tserp)
        {
        }
        // ThreadData *data = new ThreadData(sock, clientip, clientport, this);

        ~ThreadData()
        {
        }
        int _sock;
        std::string _clientip;
        std::uint16_t _clientport;
        tcp_server *_tserp; // this指针
    };

    class tcp_server
    {
    public:
        tcp_server(func_t func,uint16_t port = defaultPort) : _func(func),_port(port)
        {
        }

        void ServerInit()
        {
            
            // 1.创建套接字
            _listensock.Socket();
            // 2.绑定
            // std::string ip = INADDR_ANY; // error!!! ANADDR_ANY 是四字节整数

            _listensock.Bind(_port);
            // 3.监听
            _listensock.Listen();

            logMessage(Info, "ServerInit success...");
        }

        static void *ThreadRoutinue(void *args)
        {
            ThreadData *t = static_cast<ThreadData *>(args);
            t->_tserp->ServerIO(t->_sock, t->_clientip, t->_clientport);
            // 当对套接字连接完成时，要关闭套接字
            close(t->_sock);
        }

        void ServerIO(int sock, std::string clientip, uint16_t clientport)
        {
            std::cout << "ServerIO start..." << std::endl;
            std::string inbuf = ""; // 将tcp缓冲区内的报文，拷贝到应用层inbuf中，不断对inbuf进行检测，截取到一个完整的报文
            logMessage(Debug,"sock %d client [%s-%d]",sock,clientip.c_str(),clientport);
            while (true)
            {
                sleep(1);
                // 这一次，我们加上了协议，那么怎么保证我们能够读取上来一个完整的报文呢？
                // 不能保证，我们只能连续读，将读到的数据保存到自定义的缓冲区中
                // 然后对缓冲区不断的检测。

                // 1.read/recv
                char buf[1024] = { 0 };
                ssize_t s = recv(sock,buf,sizeof(buf) - 1,0);
                if(s <= 0)
                {
                    std::cout << "Recv failed" << errno << strerror(errno) << std::endl;
                    break;
                }
                buf[s] = 0;

                inbuf += buf; 

                // 2.获取完整的报文 - 报头+有效载荷
                ns_Protocol::Request req;
                std::string req_str;
                int n = ns_Protocol::getPackage(inbuf,&req_str);
                if(n == 0)
                    continue;

                // 3.去掉报头,分隔符  "7"\n\t"1 + 2"\n\t --> "1 + 2"
                req_str = ns_Protocol::removeHeaders(req_str,n);

                // 4.反序列化 "1 + 2" -->  1 + 2
                req.Deserialize(req_str);
                // 5.业务处理
                ns_Protocol::Response resp = _func(req);
                // 6.序列化  1  0 --> "1  0"
                std::string resp_str;
                resp.Serialize(&resp_str);
                
                // 7.添加报头
                resp_str =  ns_Protocol::addHeaders(resp_str);

                // 8.write/send
                send(sock,resp_str.c_str(),resp_str.size(),0);
            }
        }
        // 获取套接字 -- 一定是个死循环
        void Start()
        {
            logMessage(Info, "tcpServer start...");

            std::string clientip;
            std::uint16_t clientport;
            while (true)
            {
                // 4.获取新连接
                int sock = _listensock.Accept(&clientip, &clientport);
                if (sock < 0)
                {
                    logMessage(Warning, "sock error, %d %s", errno, strerror(errno));
                    continue;
                }
                // 获取到了一个新的连接
                // 用多线程的放来来为新连接提供服务
                logMessage(Info, "accept success... a new sock %d, clientMes: [%s-%d]", sock,clientip.c_str(),clientport);

                // 5.进行网络通信
                pthread_t pid;
                ThreadData *data = new ThreadData(sock, clientip, clientport, this);
                logMessage(Debug,"data:: sock %d client [%s-%d]",data->_sock,data->_clientip.c_str(),data->_clientport);
                // pthread_create(&pid, nullptr, ThreadRoutinue, &data);
                // 我！真无语  data本来就是指针，你还取地址！！！
                pthread_create(&pid, nullptr, ThreadRoutinue, data);

            }
        }

        ~tcp_server()
        {
            _listensock.Close();
        }

    private:
        Sock _listensock;
        uint16_t _port;
        func_t _func;
    };
}